/**
	Copyright (c) 2010 ATTi 
	All rights reserved.
	Author: Michael Shu
	Date: 4/16/201
*/


public without sharing class CaseBusinessDayHelper {
	//-------- Business Hours Name ------------------------------------------------
	public static String EAST = 'EAST';
	public static Boolean ISDEFAULT = true;
	//------------- For case business logic ----------------------------
	public static String OWNER = 'Owner';
	public static String STATUS = 'Status';
	public static String IR_PRIORITY = 'IR Priority';
	public static String ALL = 'All';
	public static String CASE_OPEN_DURATION = 'Open Period';
	private static map<String, String> oldValues = new map<String, String>();
	//------------------------------------------------------------------
	private static map<ID, BusinessHours> BusinessHoursMap;
	public class BusinessHoursCalcException extends Exception {}

	public static void calculateBusinessAges(Case_Cycle__c cc){
		Boolean isDefaulthb = true;
		String  hbName = null;
		/* define different Busines Hour Configuration Names based on Case Record Type Names
		if(GeneralConstants.RT_CCIR.contains(cc.ParentCase__r.RecordType.Name)){
			....
		}else if(...){
			....
		}
		*/
		DateTime endDateTime = (cc.End_Date__c == null)? Datetime.now() : cc.End_Date__c;
		cc.Age_Business_Days__c = calculateBusinessDayAge(cc.Start_Date__c, endDateTime, isDefaulthb, hbName);
		cc.Age_Business_Hours__c = calculateBusinessHourAge(cc.Start_Date__c, endDateTime, isDefaulthb, hbName);
	}
		
	private static map<ID, BusinessHours> getBusinessHours(){
		if(BusinessHoursMap == null){
			BusinessHoursMap = new map<ID, BusinessHours>([select Id, Name, MondayStartTime, MondayEndTime from businesshours order by IsDefault desc]);
		}
		return BusinessHoursMap;
	}
	
	private static map<String, ID> getBusinessHoursMapTable(){
		map<String, ID>	BusinessHoursMapTable = new map<String, ID>();
		for(BusinessHours b : getBusinessHours().values()){
			BusinessHoursMapTable.put(b.Name, b.Id);
		}
		return BusinessHoursMapTable;
	}
	
	private static Integer getBusinessHoursPerDay(ID bhour_id){
		BusinessHours sel_bh = getBusinessHours().get(bhour_id);
		Integer rst = sel_bh.MondayEndTime.hour() - sel_bh.MondayStartTime.hour(); // user Monday to calculate business hours per day
		if(rst == 0){
			throw new BusinessHoursCalcException('Please contact your admin to setup Business Hours in your envrioment');
		}
		return rst;
	}
	
	private static ID getBusinessHoursID(Boolean is_default_bh, String bh_name){
		map<String, ID> bh_table = getBusinessHoursMapTable();
		if(!is_default_bh && !bh_table.containsKey(bh_name)){
			throw new BusinessHoursCalcException('Can\'t find the Business Hours Name : ' + bh_name + ', Contact your admin to create one');
		}
		
		Id rst;
		if(is_default_bh){ 
			rst = bh_table.values()[0];
		}else{
			rst = bh_table.get(bh_name);
		}
		return rst;
	}
	
	public static Decimal calculateBusinessHourAge(DateTime openDateTime,  DateTime closeDateTime, Boolean is_default_bh, String bh_name){
		ID bh_id =  getBusinessHoursID(is_default_bh, bh_name);
		Decimal diff = (Decimal) BusinessHours.diff(bh_id, openDateTime, closeDateTime);
		Integer div = 1000*60*60;
		Decimal rst = diff.divide(div,2,System.RoundingMode.UP);
		return rst;
	} 
		
	public static Decimal calculateBusinessDayAge(DateTime openDateTime,  DateTime closeDateTime, Boolean is_default_bh, String bh_name){
		ID bh_id =  getBusinessHoursID(is_default_bh, bh_name);
		Decimal diff = (Decimal) BusinessHours.diff(bh_id, openDateTime, closeDateTime);
		Integer bHoursPerDay = getBusinessHoursPerDay(bh_id);
		Long div = 1000L*60*60*bHoursPerDay;
		Decimal rst = diff.divide(div,0,System.RoundingMode.UP);
		return rst;
	}  
	
	private static Case_Cycle__c newCaseCyles(Case c, String change_type){
		Case_Cycle__c cc = new Case_Cycle__c();
		if(change_type == Owner && c.Cycle_Team__c != null){
			cc.Value__c = c.Cycle_Team__c;
		}else if(change_type == Status && c.Status != null){
			cc.Value__c = c.Status;
		}else if(change_type == IR_Priority && c.IR_Priority__c != null){
			cc.Value__c = c.IR_Priority__c;
		}else if(change_type == ALL){
			cc.Value__c = CASE_OPEN_DURATION;
		}else{
			return null;
		}
		cc.ParentCase__c = c.Id;
		cc.Change_Type__c = change_type;
		cc.Start_Date__c = Datetime.now();
		cc.End_Date__c = null;
		cc.Age_Business_Days__c    = 0;
		cc.Age_Business_Hours__c = 0;
		return cc;
	}
		
	public static void createCaseCyles(list<Case> cases_insert_cc){
		list<Case_Cycle__c> newCClist = new list<Case_Cycle__c>();
		for(Case c : cases_insert_cc){
			Case_Cycle__c cc_owner = newCaseCyles(c,OWNER);
			Case_Cycle__c cc_status = newCaseCyles(c,STATUS);
			Case_Cycle__c cc_ir_priority = newCaseCyles(c,IR_PRIORITY);
			Case_Cycle__c cc_all = newCaseCyles(c,ALL);
			if(cc_owner != null) {newCClist.add(cc_owner);}
			if(cc_status != null) {newCClist.add(cc_status);}
			if(cc_ir_priority != null) {newCClist.add(cc_ir_priority);}
			if(cc_all != null) {newCClist.add(cc_all);}
		}
		if(!newCClist.isEmpty()){
			try{
				insert newCClist;
			}catch(Exception ex){
				System.debug('insert newCClist Fail : ' + ex);
				GeneralConstants.mailAnyExceptionToMe('CaseBusinessDayHelper', 'insert newCClist Fail', ex);			
			}
		}
	}
	
	public static void createAndupdateCaseCyles(set<ID> updated_cs_Ids){
		list<Case_Cycle__c> newCClist = new list<Case_Cycle__c>();
		list<Case_Cycle__c> updateCClist = new list<Case_Cycle__c>();
		
		list<Case> allCases = [select Id, RecordType.Name, isClosed, Cycle_Team__c, Status, IR_Priority__c, (select Change_Type__c, Start_Date__c, End_Date__c, Value__c from Case_Cycles__r where End_Date__c =: null) from Case where Id IN: updated_cs_Ids];
		for(Case c : allCases){
			list<Case_Cycle__c> allCCs = c.Case_Cycles__r;
			if(allCCs != null){
				for(Case_Cycle__c u_cc : allCCs){
					Boolean isEndDuration = (u_cc.Change_Type__c == OWNER && u_cc.Value__c != c.Cycle_Team__c)
					                     || (u_cc.Change_Type__c == STATUS && u_cc.Value__c != c.Status)
					                     || (u_cc.Change_Type__c == IR_PRIORITY && u_cc.Value__c != c.IR_Priority__c);
					if(c.isClosed || isEndDuration){
						u_cc.End_Date__c = Datetime.now();
					}
					calculateBusinessAges(u_cc);
					updateCClist.add(u_cc);	
					oldValues.put(c.Id + ';'+ u_cc.Change_Type__c, u_cc.Value__c);				
				}				
			}
			if(!c.isClosed && c.Cycle_Team__c != null && oldValues.get(c.Id + ';'+ OWNER) != c.Cycle_Team__c){
				Case_Cycle__c cc_owner = newCaseCyles(c,OWNER);
				newCClist.add(cc_owner);
				oldValues.put(c.Id + ';'+ OWNER, c.Cycle_Team__c);
			}
			if(!c.isClosed && c.Status != null && oldValues.get(c.Id + ';'+ STATUS) != c.Status){
				Case_Cycle__c cc_status = newCaseCyles(c,STATUS);
				newCClist.add(cc_status);
				oldValues.put(c.Id + ';'+ STATUS, c.Status);
			}
			if(!c.isClosed && c.IR_Priority__c != null && oldValues.get(c.Id + ';'+ IR_PRIORITY) != c.IR_Priority__c){
				Case_Cycle__c cc_ir_priority = newCaseCyles(c,IR_Priority);
				newCClist.add(cc_ir_priority);
				oldValues.put(c.Id + ';'+ IR_PRIORITY, c.IR_Priority__c);
			}
			if(!c.isClosed && oldValues.get(c.Id + ';'+ ALL) != CASE_OPEN_DURATION){ //re-open case
				Case_Cycle__c cc_all = newCaseCyles(c,ALL);
				newCClist.add(cc_all);
				oldValues.put(c.Id + ';'+ ALL, CASE_OPEN_DURATION);
			}			
		}
		
		if(!updateCClist.isEmpty()){
			try{
				update updateCClist;
			}catch(Exception ex){
				System.debug('update updateCClist Fail : ' + ex);
				GeneralConstants.mailAnyExceptionToMe('CaseBusinessDayHelper', 'update updateCClist Fail', ex);			
			}
		}
		
		if(!newCClist.isEmpty()){
			try{
				insert newCClist;
			}catch(Exception ex){
				System.debug('insert newCClist Fail : ' + ex);
				GeneralConstants.mailAnyExceptionToMe('CaseBusinessDayHelper', 'insert newCClist Fail', ex);			
			}
		}
	}	

}